// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include <stddef.h>
#include <string.h>

#include "macro_utils/macro_utils.h"

#include "umock_c/umock_lock_factory.h"
#include "umock_c/umock_lock_if.h"
#include "umock_c/umock_log.h"
#include "umock_c/umockalloc.h"
#include "umock_c/umockcallrecorder.h"
#include "umock_c/umockcall.h"

typedef struct UMOCK_EXPECTED_CALL_TAG
{
    UMOCKCALL_HANDLE umockcall;
    unsigned int is_matched : 1;
} UMOCK_EXPECTED_CALL;

typedef struct UMOCKCALLRECORDER_TAG
{
    size_t expected_call_count;
    UMOCK_EXPECTED_CALL* expected_calls;
    size_t actual_call_count;
    UMOCKCALL_HANDLE* actual_calls;
    char* expected_calls_string;
    char* actual_calls_string;
    UMOCK_C_LOCK_FACTORY_CREATE_LOCK_FUNC lock_factory_create_lock;
    void* lock_factory_create_lock_params;
    UMOCK_C_LOCK_HANDLE lock;
} UMOCKCALLRECORDER;

static void internal_lock_acquire_exclusive_if_needed(UMOCKCALLRECORDER_HANDLE call_recorder)
{
    if (call_recorder->lock != NULL)
    {
        call_recorder->lock->acquire_exclusive(call_recorder->lock);
    }
}

static void internal_lock_acquire_shared_if_needed(UMOCKCALLRECORDER_HANDLE call_recorder)
{
    if (call_recorder->lock != NULL)
    {
        call_recorder->lock->acquire_shared(call_recorder->lock);
    }
}

static void internal_lock_release_exclusive_if_needed(UMOCKCALLRECORDER_HANDLE call_recorder)
{
    if (call_recorder->lock != NULL)
    {
        call_recorder->lock->release_exclusive(call_recorder->lock);
    }
}

static void internal_lock_release_shared_if_needed(UMOCKCALLRECORDER_HANDLE call_recorder)
{
    if (call_recorder->lock != NULL)
    {
        call_recorder->lock->release_shared(call_recorder->lock);
    }
}

static void internal_umockcallrecorder_reset_all_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder)
{
    /* Codes_SRS_UMOCKCALLRECORDER_01_005: [ umockcallrecorder_reset_all_calls shall free all the expected and actual calls for the call recorder identified by umock_call_recorder. ]*/
    if (umock_call_recorder->expected_calls != NULL)
    {
        size_t i;
        for (i = 0; i < umock_call_recorder->expected_call_count; i++)
        {
            umockcall_destroy(umock_call_recorder->expected_calls[i].umockcall);
        }

        umockalloc_free(umock_call_recorder->expected_calls);
        umock_call_recorder->expected_calls = NULL;
    }
    umock_call_recorder->expected_call_count = 0;

    if (umock_call_recorder->actual_calls != NULL)
    {
        size_t i;
        for (i = 0; i < umock_call_recorder->actual_call_count; i++)
        {
            umockcall_destroy(umock_call_recorder->actual_calls[i]);
        }

        umockalloc_free(umock_call_recorder->actual_calls);
        umock_call_recorder->actual_calls = NULL;
    }
    umock_call_recorder->actual_call_count = 0;
}

UMOCKCALLRECORDER_HANDLE umockcallrecorder_create(UMOCK_C_LOCK_FACTORY_CREATE_LOCK_FUNC lock_factory_create_lock, void* lock_factory_create_lock_params)
{
    UMOCKCALLRECORDER_HANDLE result;

    /* Codes_SRS_UMOCKCALLRECORDER_01_095: [ lock_factory_create_lock may be NULL. ]*/
    /* Codes_SRS_UMOCKCALLRECORDER_01_001: [ umockcallrecorder_create shall create a new instance of a call recorder and return a non-NULL handle to it on success. ]*/
    result = umockalloc_malloc(sizeof(UMOCKCALLRECORDER));
    if (result == NULL)
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_002: [ If any error occurs, umockcallrecorder_create shall return NULL. ]*/
        UMOCK_LOG("umockalloc_malloc(%zu) failed", sizeof(UMOCKCALLRECORDER));
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_097: [ If lock_factory_create_lock is not NULL, umockcallrecorder_create shall call lock_factory_create_lock to create the lock used when working with the stored calls. ]*/
        if (
            (lock_factory_create_lock != NULL) && 
            ((result->lock = lock_factory_create_lock(lock_factory_create_lock_params)) == NULL)
            )
        {
            /* Tests_SRS_UMOCKCALLRECORDER_01_002: [ If any error occurs, umockcallrecorder_create shall return NULL. ]*/
            UMOCK_LOG("lock_factory_create_lock failed");
        }
        else
        {
            if (lock_factory_create_lock == NULL)
            {
                result->lock = NULL;
            }

            result->expected_call_count = 0;
            result->expected_calls = NULL;
            result->expected_calls_string = NULL;
            result->actual_call_count = 0;
            result->actual_calls = NULL;
            result->actual_calls_string = NULL;

            /* Codes_SRS_UMOCKCALLRECORDER_01_096: [ lock_factory_create_lock shall be saved for later use. ]*/
            result->lock_factory_create_lock = lock_factory_create_lock;
            result->lock_factory_create_lock_params = lock_factory_create_lock_params;

            goto all_ok;
        }

        umockalloc_free(result);
        result = NULL;
    }

all_ok:
    return result;
}

void umockcallrecorder_destroy(UMOCKCALLRECORDER_HANDLE umock_call_recorder)
{
    /* Codes_SRS_UMOCKCALLRECORDER_01_004: [ If umock_call_recorder is NULL, umockcallrecorder_destroy shall do nothing. ]*/
    if (umock_call_recorder != NULL)
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_003: [ umockcallrecorder_destroy shall free the resources associated with a the call recorder identified by the umock_call_recorder argument. ]*/
        internal_umockcallrecorder_reset_all_calls(umock_call_recorder);
        if (umock_call_recorder->actual_calls_string != NULL)
        {
            umockalloc_free(umock_call_recorder->actual_calls_string);
        }
        if (umock_call_recorder->expected_calls_string != NULL)
        {
            umockalloc_free(umock_call_recorder->expected_calls_string);
        }
        if (umock_call_recorder->lock != NULL)
        {
            umock_call_recorder->lock->destroy(umock_call_recorder->lock);
        }
        umockalloc_free(umock_call_recorder);
    }
}

int umockcallrecorder_reset_all_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder)
{
    int result;

    if (umock_call_recorder == NULL)
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_007: [ If umock_call_recorder is NULL, umockcallrecorder_reset_all_calls shall fail and return a non-zero value. ]*/
        UMOCK_LOG("umockcallrecorder: Reset all calls failed: NULL umock_call_recorder.");
        result = MU_FAILURE;
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_065: [ If a lock was created for the call recorder, umockcallrecorder_reset_all_calls acquire the lock in exclusive mode. ]*/
        internal_lock_acquire_exclusive_if_needed(umock_call_recorder);
        {
            internal_umockcallrecorder_reset_all_calls(umock_call_recorder);

            /* Codes_SRS_UMOCKCALLRECORDER_01_066: [ If a lock was created for the call recorder, umockcallrecorder_reset_all_calls shall release the exclusive lock. ]*/
            internal_lock_release_exclusive_if_needed(umock_call_recorder);

            /* Codes_SRS_UMOCKCALLRECORDER_01_006: [ On success umockcallrecorder_reset_all_calls shall return 0. ]*/
            result = 0;
        }
    }

    return result;
}

int umockcallrecorder_add_expected_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, UMOCKCALL_HANDLE mock_call)
{
    int result;

    if ((umock_call_recorder == NULL) ||
        (mock_call == NULL))
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_012: [ If any of the arguments is NULL, umockcallrecorder_add_expected_call shall fail and return a non-zero value. ]*/
        UMOCK_LOG("umockcallrecorder: Bad arguments in add expected call: umock_call_recorder = %p, mock_call = %p.",
            umock_call_recorder, mock_call);
        result = MU_FAILURE;
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_068: [ If a lock was created for the call recorder, umockcallrecorder_add_expected_call acquire the lock in exclusive mode. ]*/
        internal_lock_acquire_exclusive_if_needed(umock_call_recorder);
        {
            UMOCK_EXPECTED_CALL* new_expected_calls = umockalloc_realloc(umock_call_recorder->expected_calls, sizeof(UMOCK_EXPECTED_CALL) * (umock_call_recorder->expected_call_count + 1));
            if (new_expected_calls == NULL)
            {
                /* Codes_SRS_UMOCKCALLRECORDER_01_013: [ If any error occurs, umockcallrecorder_add_expected_call shall fail and return a non-zero value. ]*/
                UMOCK_LOG("umockcallrecorder: Cannot allocate memory in add expected call.");
                result = MU_FAILURE;
            }
            else
            {
                /* Codes_SRS_UMOCKCALLRECORDER_01_008: [ umockcallrecorder_add_expected_call shall add the mock_call call to the expected call list maintained by the call recorder identified by umock_call_recorder. ]*/
                umock_call_recorder->expected_calls = new_expected_calls;
                umock_call_recorder->expected_calls[umock_call_recorder->expected_call_count].umockcall = mock_call;
                umock_call_recorder->expected_calls[umock_call_recorder->expected_call_count++].is_matched = 0;

                /* Codes_SRS_UMOCKCALLRECORDER_01_009: [ On success umockcallrecorder_add_expected_call shall return 0. ]*/
                result = 0;
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_069: [ If a lock was created for the call recorder, umockcallrecorder_add_expected_call shall release the exclusive lock. ]*/
            internal_lock_release_exclusive_if_needed(umock_call_recorder);
        }
    }

    return result;
}

int umockcallrecorder_add_actual_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, UMOCKCALL_HANDLE mock_call, UMOCKCALL_HANDLE* matched_call)
{
    int result;

    if ((umock_call_recorder == NULL) ||
        (mock_call == NULL) ||
        (matched_call == NULL))
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_019: [ If any of the arguments is NULL, umockcallrecorder_add_actual_call shall fail and return a non-zero value. ]*/
        UMOCK_LOG("umockcallrecorder: Bad arguments in add actual call: umock_call_recorder = %p, mock_call = %p, matched_call = %p.",
            umock_call_recorder, mock_call, matched_call);
        result = MU_FAILURE;
    }
    else
    {
        size_t i;
        unsigned int is_error = 0;

        *matched_call = NULL;

        /* Codes_SRS_UMOCKCALLRECORDER_01_071: [ If a lock was created for the call recorder, umockcallrecorder_add_actual_call acquire the lock in exclusive mode. ]*/
        internal_lock_acquire_exclusive_if_needed(umock_call_recorder);
        {
            /* Codes_SRS_UMOCKCALLRECORDER_01_014: [ umockcallrecorder_add_actual_call shall check whether the call mock_call matches any of the expected calls maintained by umock_call_recorder. ]*/
            /* Codes_SRS_UMOCK_C_LIB_01_115: [ umock_c shall compare calls in order. ]*/
            for (i = 0; i < umock_call_recorder->expected_call_count; i++)
            {
                int ignore_all_calls = umockcall_get_ignore_all_calls(umock_call_recorder->expected_calls[i].umockcall);
                if (ignore_all_calls < 0)
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_058: [ If getting ignore_all_calls by calling umockcall_get_ignore_all_calls fails, umockcallrecorder_add_actual_call shall fail and return a non-zero value. ]*/
                    UMOCK_LOG("umockcallrecorder: Cannot get the ignore_all_calls flag.");
                    is_error = 1;
                    break;
                }
                else
                {
                    if ((umock_call_recorder->expected_calls[i].is_matched == 0) ||
                        /* Codes_SRS_UMOCKCALLRECORDER_01_057: [ If any expected call has ignore_all_calls set and the actual call is equal to it when comparing the 2 calls, then the call shall be considered matched and not added to the actual calls list. ]*/
                        (ignore_all_calls > 0))
                    {
                        /* Codes_SRS_UMOCKCALLRECORDER_01_017: [ Comparing the calls shall be done by calling umockcall_are_equal. ]*/
                        int are_equal_result = umockcall_are_equal(umock_call_recorder->expected_calls[i].umockcall, mock_call);
                        if (are_equal_result == 1)
                        {
                            /* Codes_SRS_UMOCKCALLRECORDER_01_016: [ If the call matches one of the expected calls, a handle to the matched call shall be filled into the matched_call argument. ]*/
                            *matched_call = umock_call_recorder->expected_calls[i].umockcall;
                            break;
                        }
                        /* Codes_SRS_UMOCKCALLRECORDER_01_021: [ If umockcall_are_equal fails, umockcallrecorder_add_actual_call shall fail and return a non-zero value. ]*/
                        else if (are_equal_result != 0)
                        {
                            is_error = 1;
                            break;
                        }
                        else
                        {
                            if (ignore_all_calls == 0)
                            {
                                i = umock_call_recorder->expected_call_count;
                                break;
                            }
                        }
                    }
                }
            }

            if ((umock_call_recorder->actual_call_count == 0) && (i < umock_call_recorder->expected_call_count))
            {
                umock_call_recorder->expected_calls[i].is_matched = 1;
            }
            else
            {
                i = umock_call_recorder->expected_call_count;
            }

            if (is_error)
            {
                UMOCK_LOG("umockcallrecorder: Error in finding a matched call.");
                result = MU_FAILURE;
            }
            else
            {
                if (i == umock_call_recorder->expected_call_count)
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_015: [ If the call does not match any of the expected calls, then umockcallrecorder_add_actual_call shall add the mock_call call to the actual call list maintained by umock_call_recorder. ]*/
                    /* an unexpected call */
                    UMOCKCALL_HANDLE* new_actual_calls = umockalloc_realloc(umock_call_recorder->actual_calls, sizeof(UMOCKCALL_HANDLE) * (umock_call_recorder->actual_call_count + 1));
                    if (new_actual_calls == NULL)
                    {
                        /* Codes_SRS_UMOCKCALLRECORDER_01_020: [ If allocating memory for the actual calls fails, umockcallrecorder_add_actual_call shall fail and return a non-zero value. ]*/
                        UMOCK_LOG("umockcallrecorder: Cannot allocate memory for actual calls.");
                        result = MU_FAILURE;
                    }
                    else
                    {
                        umock_call_recorder->actual_calls = new_actual_calls;
                        umock_call_recorder->actual_calls[umock_call_recorder->actual_call_count++] = mock_call;

                        /* Codes_SRS_UMOCKCALLRECORDER_01_018: [ When no error is encountered, umockcallrecorder_add_actual_call shall return 0. ]*/
                        result = 0;
                    }
                }
                else
                {
                    umockcall_destroy(mock_call);

                    /* Codes_SRS_UMOCKCALLRECORDER_01_018: [ When no error is encountered, umockcallrecorder_add_actual_call shall return 0. ]*/
                    result = 0;
                }
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_070: [ If a lock was created for the call recorder, umockcallrecorder_add_actual_call shall release the exclusive lock. ]*/
            internal_lock_release_exclusive_if_needed(umock_call_recorder);
        }
    }

    return result;
}

const char* umockcallrecorder_get_expected_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder)
{
    const char* result;

    if (umock_call_recorder == NULL)
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_029: [ If the umock_call_recorder is NULL, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/
        UMOCK_LOG("umockcallrecorder: NULL umock_call_recorder in get expected calls.");
        result = NULL;
    }
    else
    {
        size_t i;
        char* new_expected_calls_string;
        size_t current_length = 0;

        /* Codes_SRS_UMOCKCALLRECORDER_01_076: [ If a lock was created for the call recorder, umockcallrecorder_get_expected_calls shall acquire the lock in exclusive mode. ]*/
        internal_lock_acquire_exclusive_if_needed(umock_call_recorder);
        {
            for (i = 0; i < umock_call_recorder->expected_call_count; i++)
            {
                /* Codes_SRS_UMOCKCALLRECORDER_01_055: [ Getting the ignore_all_calls property shall be done by calling umockcall_get_ignore_all_calls. ]*/
                int ignore_all_calls = umockcall_get_ignore_all_calls(umock_call_recorder->expected_calls[i].umockcall);
                if (ignore_all_calls < 0)
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_056: [ If umockcall_get_ignore_all_calls returns a negative value then umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/
                    UMOCK_LOG("umockcallrecorder: Cannot get the ignore_all_calls flag.");
                    break;
                }
                else
                {
                    if ((umock_call_recorder->expected_calls[i].is_matched == 0) &&
                        /* Codes_SRS_UMOCKCALLRECORDER_01_054: [ Calls that have the ignore_all_calls property set shall not be reported in the expected call list. ]*/
                        (ignore_all_calls == 0))
                    {
                        /* Codes_SRS_UMOCKCALLRECORDER_01_028: [ The string for each call shall be obtained by calling umockcall_stringify. ]*/
                        char* stringified_call = umockcall_stringify(umock_call_recorder->expected_calls[i].umockcall);
                        if (stringified_call == NULL)
                        {
                            /* Codes_SRS_UMOCKCALLRECORDER_01_030: [ If umockcall_stringify fails, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/
                            break;
                        }
                        else
                        {
                            size_t stringified_call_length = strlen(stringified_call);
                            new_expected_calls_string = umockalloc_realloc(umock_call_recorder->expected_calls_string, current_length + stringified_call_length + 1);
                            if (new_expected_calls_string == NULL)
                            {
                                umockalloc_free(stringified_call);

                                /* Codes_SRS_UMOCKCALLRECORDER_01_031: [ If allocating memory for the resulting string fails, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/
                                break;
                            }
                            else
                            {
                                umock_call_recorder->expected_calls_string = new_expected_calls_string;
                                (void)memcpy(umock_call_recorder->expected_calls_string + current_length, stringified_call, stringified_call_length + 1);
                                current_length += stringified_call_length;
                            }

                            umockalloc_free(stringified_call);
                        }
                    }
                }
            }

            if (i < umock_call_recorder->expected_call_count)
            {
                result = NULL;
            }
            else
            {
                if (current_length == 0)
                {
                    new_expected_calls_string = umockalloc_realloc(umock_call_recorder->expected_calls_string, 1);
                    if (new_expected_calls_string == NULL)
                    {
                        /* Codes_SRS_UMOCKCALLRECORDER_01_031: [ If allocating memory for the resulting string fails, umockcallrecorder_get_expected_calls shall fail and return NULL. ]*/
                        UMOCK_LOG("umockcallrecorder: Cannot allocate memory for expected calls.");
                        result = NULL;
                    }
                    else
                    {
                        umock_call_recorder->expected_calls_string = new_expected_calls_string;
                        umock_call_recorder->expected_calls_string[0] = '\0';

                        /* Codes_SRS_UMOCKCALLRECORDER_01_027: [ umockcallrecorder_get_expected_calls shall return a pointer to the string representation of all the expected calls. ]*/
                        result = umock_call_recorder->expected_calls_string;
                    }
                }
                else
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_027: [ umockcallrecorder_get_expected_calls shall return a pointer to the string representation of all the expected calls. ]*/
                    result = umock_call_recorder->expected_calls_string;
                }
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_077: [ If a lock was created for the call recorder, umockcallrecorder_get_expected_calls shall release the exclusive lock. ]*/
            internal_lock_release_exclusive_if_needed(umock_call_recorder);
        }
    }

    return result;
}

const char* umockcallrecorder_get_actual_calls(UMOCKCALLRECORDER_HANDLE umock_call_recorder)
{
    const char* result;

    if (umock_call_recorder == NULL)
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_024: [ If the umock_call_recorder is NULL, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/
        UMOCK_LOG("umockcallrecorder: NULL umock_call_recorder in get actual calls.");
        result = NULL;
    }
    else
    {
        size_t i;
        char* new_actual_calls_string;

        /* Codes_SRS_UMOCKCALLRECORDER_01_073: [ If a lock was created for the call recorder, umockcallrecorder_get_actual_calls shall acquire the lock in shared mode. ]*/
        internal_lock_acquire_exclusive_if_needed(umock_call_recorder);
        {
            if (umock_call_recorder->actual_call_count == 0)
            {
                new_actual_calls_string = umockalloc_realloc(umock_call_recorder->actual_calls_string, 1);
                if (new_actual_calls_string == NULL)
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_026: [ If allocating memory for the resulting string fails, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/
                    UMOCK_LOG("umockcallrecorder: Cannot allocate memory for actual calls.");
                    result = NULL;
                }
                else
                {
                    umock_call_recorder->actual_calls_string = new_actual_calls_string;
                    umock_call_recorder->actual_calls_string[0] = '\0';

                    /* Codes_SRS_UMOCKCALLRECORDER_01_022: [ umockcallrecorder_get_actual_calls shall return a pointer to the string representation of all the actual calls. ]*/
                    result = umock_call_recorder->actual_calls_string;
                }
            }
            else
            {
                size_t current_length = 0;

                for (i = 0; i < umock_call_recorder->actual_call_count; i++)
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_023: [ The string for each call shall be obtained by calling umockcall_stringify. ]*/
                    char* stringified_call = umockcall_stringify(umock_call_recorder->actual_calls[i]);
                    if (stringified_call == NULL)
                    {
                        /* Codes_SRS_UMOCKCALLRECORDER_01_025: [ If umockcall_stringify fails, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/
                        break;
                    }
                    else
                    {
                        size_t stringified_call_length = strlen(stringified_call);
                        new_actual_calls_string = umockalloc_realloc(umock_call_recorder->actual_calls_string, current_length + stringified_call_length + 1);
                        if (new_actual_calls_string == NULL)
                        {
                            umockalloc_free(stringified_call);

                            /* Codes_SRS_UMOCKCALLRECORDER_01_026: [ If allocating memory for the resulting string fails, umockcallrecorder_get_actual_calls shall fail and return NULL. ]*/
                            break;
                        }
                        else
                        {
                            umock_call_recorder->actual_calls_string = new_actual_calls_string;
                            (void)memcpy(umock_call_recorder->actual_calls_string + current_length, stringified_call, stringified_call_length + 1);
                            current_length += stringified_call_length;
                        }

                        umockalloc_free(stringified_call);
                    }
                }

                if (i < umock_call_recorder->actual_call_count)
                {
                    result = NULL;
                }
                else
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_022: [ umockcallrecorder_get_actual_calls shall return a pointer to the string representation of all the actual calls. ]*/
                    result = umock_call_recorder->actual_calls_string;
                }
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_074: [ If a lock was created for the call recorder, umockcallrecorder_get_actual_calls shall release the exclusive lock. ]*/
            internal_lock_release_exclusive_if_needed(umock_call_recorder);
        }
    }

    return result;
}

UMOCKCALL_HANDLE umockcallrecorder_get_last_expected_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder)
{
    UMOCKCALL_HANDLE result;

    if (umock_call_recorder == NULL)
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_033: [ If umock_call_recorder is NULL, umockcallrecorder_get_last_expected_call shall fail and return NULL. ]*/
        UMOCK_LOG("umockcallrecorder: NULL umock_call_recorder in get last expected calls.");
        result = NULL;
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_079: [ If a lock was created for the call recorder, umockcallrecorder_get_last_expected_call shall acquire the lock in shared mode. ]*/
        internal_lock_acquire_shared_if_needed(umock_call_recorder);
        {
            if (umock_call_recorder->expected_call_count == 0)
            {
                /* Codes_SRS_UMOCKCALLRECORDER_01_034: [ If no expected call has been recorded for umock_call_recorder then umockcallrecorder_get_last_expected_call shall fail and return NULL. ]*/
                UMOCK_LOG("umockcallrecorder: No expected calls recorded.");
                result = NULL;
            }
            else
            {
                /* Codes_SRS_UMOCKCALLRECORDER_01_032: [ umockcallrecorder_get_last_expected_call shall return the last expected call for the umock_call_recorder call recorder. ]*/
                result = umock_call_recorder->expected_calls[umock_call_recorder->expected_call_count - 1].umockcall;
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_080: [ If a lock was created for the call recorder, umockcallrecorder_get_last_expected_call shall release the shared lock. ]*/
            internal_lock_release_shared_if_needed(umock_call_recorder);
        }
    }

    return result;
}

/* Codes_SRS_UMOCKCALLRECORDER_01_035: [ umockcallrecorder_clone shall clone a call recorder and return a handle to the newly cloned call recorder. ]*/
UMOCKCALLRECORDER_HANDLE umockcallrecorder_clone(UMOCKCALLRECORDER_HANDLE umock_call_recorder)
{
    UMOCKCALLRECORDER_HANDLE result;

    if (umock_call_recorder == NULL)
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_036: [ If the umock_call_recorder argument is NULL, umockcallrecorder_clone shall fail and return NULL. ]*/
        UMOCK_LOG("umockcallrecorder_clone: NULL umock_call_recorder.");
        result = NULL;
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_082: [ If a lock was created for the call recorder umock_call_recorder, umockcallrecorder_clone shall acquire_the lock in shared mode for umock_call_recorder. ]*/
        internal_lock_acquire_shared_if_needed(umock_call_recorder);
        {
            /* Codes_SRS_UMOCKCALLRECORDER_01_037: [ If allocating memory for the new umock call recorder instance fails, umockcallrecorder_clone shall fail and return NULL. ]*/
            /* Codes_SRS_UMOCKCALLRECORDER_01_085: [ If the lock_factory_create_lock associated with umock_call_recorder is not NULL, umockcallrecorder_clone shall create a new lock for the cloned call recorder. ]*/
            result = umockcallrecorder_create(umock_call_recorder->lock_factory_create_lock, umock_call_recorder->lock_factory_create_lock_params);
            if (result != NULL)
            {
                size_t i;

                result->expected_calls = umockalloc_malloc(sizeof(UMOCK_EXPECTED_CALL) * umock_call_recorder->expected_call_count);
                if (result->expected_calls == NULL)
                {
                    /* Codes_SRS_UMOCKCALLRECORDER_01_052: [ If any error occurs, umockcallrecorder_clone shall fail and return NULL. ]*/
                    umockcallrecorder_destroy(result);
                    result = NULL;
                    UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot allocate expected_calls.");
                }
                else
                {
                    for (i = 0; i < umock_call_recorder->expected_call_count; i++)
                    {
                        result->expected_calls[i].umockcall = NULL;
                    }

                    /* Codes_SRS_UMOCKCALLRECORDER_01_038: [ umockcallrecorder_clone shall clone all the expected calls. ]*/
                    for (i = 0; i < umock_call_recorder->expected_call_count; i++)
                    {
                        result->expected_calls[i].is_matched = umock_call_recorder->expected_calls[i].is_matched;

                        /* Codes_SRS_UMOCKCALLRECORDER_01_039: [ Each expected call shall be cloned by calling umockcall_clone. ]*/
                        result->expected_calls[i].umockcall = umockcall_clone(umock_call_recorder->expected_calls[i].umockcall);
                        if (result->expected_calls[i].umockcall == NULL)
                        {
                            break;
                        }
                    }

                    if (i < umock_call_recorder->expected_call_count)
                    {
                        size_t j;

                        /* Codes_SRS_UMOCKCALLRECORDER_01_040: [ If cloning an expected call fails, umockcallrecorder_clone shall fail and return NULL. ]*/
                        for (j = 0; j < i; j++)
                        {
                            umockcall_destroy(result->expected_calls[j].umockcall);
                            result->expected_calls[j].umockcall = NULL;
                        }

                        umockcallrecorder_destroy(result);
                        result = NULL;
                        UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot clone all expected calls.");
                    }
                    else
                    {
                        result->expected_call_count = umock_call_recorder->expected_call_count;

                        result->actual_calls = umockalloc_malloc(sizeof(UMOCKCALL_HANDLE) * umock_call_recorder->actual_call_count);
                        if (result->actual_calls == NULL)
                        {
                            /* Codes_SRS_UMOCKCALLRECORDER_01_052: [ If any error occurs, umockcallrecorder_clone shall fail and return NULL. ]*/
                            umockcallrecorder_destroy(result);
                            result = NULL;
                            UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot allocate expected_calls.");
                        }
                        else
                        {
                            for (i = 0; i < umock_call_recorder->actual_call_count; i++)
                            {
                                result->actual_calls[i] = NULL;
                            }

                            /* Codes_SRS_UMOCKCALLRECORDER_01_041: [ umockcallrecorder_clone shall clone all the actual calls. ]*/
                            for (i = 0; i < umock_call_recorder->actual_call_count; i++)
                            {
                                /* Codes_SRS_UMOCKCALLRECORDER_01_042: [ Each actual call shall be cloned by calling umockcall_clone. ]*/
                                result->actual_calls[i] = umockcall_clone(umock_call_recorder->actual_calls[i]);
                                if (result->actual_calls[i] == NULL)
                                {
                                    break;
                                }
                            }

                            if (i < umock_call_recorder->actual_call_count)
                            {
                                size_t j;

                                /* Codes_SRS_UMOCKCALLRECORDER_01_043: [ If cloning an actual call fails, umockcallrecorder_clone shall fail and return NULL. ]*/
                                for (j = 0; j < i; j++)
                                {
                                    umockcall_destroy(result->actual_calls[j]);
                                    result->actual_calls[j] = NULL;
                                }

                                umockcallrecorder_destroy(result);
                                result = NULL;
                                UMOCK_LOG("umockcallrecorder: clone call recorder failed - cannot clone all actual calls.");
                            }
                            else
                            {
                                result->actual_call_count = umock_call_recorder->actual_call_count;
                            }
                        }
                    }
                }
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_083: [ If a lock was created for the call recorder umock_call_recorder, umockcallrecorder_clone shall release the shared lock for umock_call_recorder. ]*/
            internal_lock_release_shared_if_needed(umock_call_recorder);
        }
    }

    return result;
}

int umockcallrecorder_get_expected_call_count(UMOCKCALLRECORDER_HANDLE umock_call_recorder, size_t* expected_call_count)
{
    int result;

    if ((umock_call_recorder == NULL) ||
        (expected_call_count == NULL))
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_046: [ If any of the arguments is NULL, umockcallrecorder_get_expected_call_count shall return a non-zero value. ]*/
        result = MU_FAILURE;
        UMOCK_LOG("umockcallrecorder_get_expected_call_count: Invalid arguments, umock_call_recorder = %p, expected_call_count = %p",
            umock_call_recorder, expected_call_count);
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_086: [ If a lock was created for the call recorder, umockcallrecorder_get_expected_call_count shall acquire the lock in shared mode. ]*/
        internal_lock_acquire_shared_if_needed(umock_call_recorder);
        {
            /* Codes_SRS_UMOCKCALLRECORDER_01_044: [ umockcallrecorder_get_expected_call_count shall return in the expected_call_count argument the number of expected calls associated with the call recorder. ]*/
            *expected_call_count = umock_call_recorder->expected_call_count;
            /* Codes_SRS_UMOCKCALLRECORDER_01_045: [ On success umockcallrecorder_get_expected_call_count shall return 0. ]*/
            result = 0;

            /* Codes_SRS_UMOCKCALLRECORDER_01_087: [ If a lock was created for the call recorder, umockcallrecorder_get_expected_call_count shall release the shared lock. ]*/
            internal_lock_release_shared_if_needed(umock_call_recorder);
        }
    }

    return result;
}

int umockcallrecorder_fail_call(UMOCKCALLRECORDER_HANDLE umock_call_recorder, size_t index)
{
    int result;

    /* Codes_SRS_UMOCKCALLRECORDER_01_049: [ If umock_call_recorder is NULL, umockcallrecorder_fail_call shall return a non-zero value. ]*/
    if ((umock_call_recorder == NULL) ||
        /* Codes_SRS_UMOCKCALLRECORDER_01_050: [ If index is invalid, umockcallrecorder_fail_call shall return a non-zero value. ]*/
        (index >= umock_call_recorder->expected_call_count))
    {
        result = MU_FAILURE;
        UMOCK_LOG("umockcallrecorder_fail_call: NULL Invalid arguments, umock_call_recorder = %p, index = %zu",
            umock_call_recorder, index);
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_089: [ If a lock was created for the call recorder, umockcallrecorder_fail_call acquire the lock in exclusive mode. ]*/
        internal_lock_acquire_exclusive_if_needed(umock_call_recorder);
        {
            /* Codes_SRS_UMOCKCALLRECORDER_01_047: [ umockcallrecorder_fail_call shall mark an expected call as to be failed by calling umockcall_set_fail_call with a 1 value for fail_call. ]*/
            if (umockcall_set_fail_call(umock_call_recorder->expected_calls[index].umockcall, 1) != 0)
            {
                /* Codes_SRS_UMOCKCALLRECORDER_01_051: [ If umockcall_set_fail_call fails, umockcallrecorder_fail_call shall return a non-zero value. ]*/
                result = MU_FAILURE;
                UMOCK_LOG("umockcallrecorder_fail_call: umockcall_set_fail_call failed.");
            }
            else
            {
                /* Codes_SRS_UMOCKCALLRECORDER_01_048: [ On success, umockcallrecorder_fail_call shall return 0. ]*/
                result = 0;
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_090: [ If a lock was created for the call recorder, umockcallrecorder_fail_call shall release the exclusive lock. ]*/
            internal_lock_release_exclusive_if_needed(umock_call_recorder);
        }
    }

    return result;
}

int umockcallrecorder_can_call_fail(UMOCKCALLRECORDER_HANDLE umock_call_recorder, size_t index, int* can_call_fail)
{
    int result;

    /* Codes_SRS_UMOCKCALLRECORDER_31_056: [ If umock_call_recorder or call_can_fail is NULL, umockcallrecorder_can_call_fail shall return a non-zero value. ]*/
    if ((umock_call_recorder == NULL) ||
        (can_call_fail == NULL) ||
        /* Codes_SRS_UMOCKCALLRECORDER_31_057: [ If index is greater or equal to the current expected calls count, umockcallrecorder_can_call_fail shall return a non-zero value. ]*/
        (index >= umock_call_recorder->expected_call_count))
    {
        result = MU_FAILURE;
        UMOCK_LOG("umockcallrecorder_can_call_fail: NULL Invalid arguments, umock_call_recorder = %p, index = %zu",
            umock_call_recorder, index);
    }
    else
    {
        /* Codes_SRS_UMOCKCALLRECORDER_01_092: [ If a lock was created for the call recorder, umockcallrecorder_can_call_fail acquire the lock in exclusive mode. ]*/
        internal_lock_acquire_shared_if_needed(umock_call_recorder);
        {
            /* Codes_SRS_UMOCKCALLRECORDER_31_061: [ umockcallrecorder_can_call_fail shall determine whether given call can fail or not by calling umockcall_get_call_can_fail. ]*/
            int can_call_fail_result = umockcall_get_call_can_fail(umock_call_recorder->expected_calls[index].umockcall);
            if (can_call_fail_result == -1)
            {
                /* Codes_SRS_UMOCKCALLRECORDER_31_058: [ If umockcall_get_call_can_fail returns -1, umockcallrecorder_can_call_fail shall return an error to the caller  ]*/
                result = MU_FAILURE;
                UMOCK_LOG("umockcallrecorder_fail_call: umockcall_set_fail_call failed.");
            }
            else
            {
                /* Codes_SRS_UMOCKCALLRECORDER_31_059: [ umockcallrecorder_can_call_fail shall return in the can_call_fail argument whether the call can fail or not. ]*/
                *can_call_fail = can_call_fail_result;
                /* Codes_SRS_UMOCKCALLRECORDER_31_060: [ On success umockcallrecorder_can_call_fail shall return 0. ]*/
                result = 0;
            }

            /* Codes_SRS_UMOCKCALLRECORDER_01_094: [ If a lock was created for the call recorder, umockcallrecorder_can_call_fail shall release the exclusive lock. ]*/
            internal_lock_release_shared_if_needed(umock_call_recorder);
        }
    }

    return result;
}
